#include <stdio.h>
#include "driver/i2c.h"

#include "oled.h"

static const uint8_t oled_init_cmd[] = { 
    0x80,0xAE,//--turn off oled panel
    0x80,0x00,//---set low column address
    0x80,0x10,//---set high column address
    0x80,0x40,//--set start line address  Set Mapping RAM Display Start Line
    0x80,0x81,//--set contrast control register
    0x80,0xCF,// Set SEG Output Current Brightness
    0x80,0xA1,//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    0x80,0xC8,//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    0x80,0xA6,//--set normal display
    0x80,0xA8,//--set multiplex ratio(1 to 64)
    0x80,0x3F,//--1/64 duty
    0x80,0xD3,//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
    0x80,0x00,//-not offset
    0x80,0xD5,//--set display clock divide ratio/oscillator frequency
    0x80,0x80,//--set divide ratio, Set Clock as 100 Frames/Sec
    0x80,0xD9,//--set pre-charge period
    0x80,0xF1,//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    0x80,0xDA,//--set com pins hardware configuration
    0x80,0x12,//
    0x80,0xDB,//--set vcomh
    0x80,0x40,//Set VCOM Deselect Level
    0x80,0x20,//-Set Page Addressing Mode (0x00/0x01/0x02)
    0x80,0x02,//
    0x80,0x8D,//--set Charge Pump enable/disable
    0x80,0x14,//--set(0x10) disable
    0x80,0xA4,// Disable Entire Display On (0xa4/0xa5)
    0x80,0xA6,// Disable Inverse Display On (0xa6/a7) 
    0x00,0xAF //  
}; 

static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, const uint8_t *data_wr, size_t size)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (OLED_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

esp_err_t oled_write(const uint8_t *data_wr, size_t size)
{
	return i2c_master_write_slave(OLED_I2C_POER_NUM, data_wr, size);
}

esp_err_t oled_set_start_address(uint8_t page_addr, uint16_t ram_addr)
{
    esp_err_t esp_err;
    int i;
    uint8_t set_addr_cmd[6] = {
        0x00,0xb0,// Set Page Start Address 
        0x00,0x00,// Set Lower Column Start Address
        0x00,0x10 // Set Higher Column Start Address
        };
    set_addr_cmd[1] = 0xb0 + (page_addr & 0x07);
    set_addr_cmd[3] = 0x00 + (ram_addr & 0x0f); 
    set_addr_cmd[5] = 0x10 + ((ram_addr >> 4) & 0x0f);
    for (i = 0; i < 3; i++)
        esp_err = oled_write(set_addr_cmd + i * 2, 2);
    return esp_err;
}

void oled_clear_page(int page)
{
	uint8_t buf[129] = {0};
	buf[0] = 0x40;
	oled_set_start_address(page, 0);
	oled_write(buf, 128+1);
}

void oled_clear(void)
{
	int i = 0;

	for (i = 0; i < 8; i++) {
		oled_clear_page(i);
	}
}

esp_err_t oled_init()
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = OLED_I2C_SDA_IO,         // select GPIO specific to your project
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = OLED_I2C_SCL_IO,         // select GPIO specific to your project
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = OLED_I2C_FREQ,  // select frequency specific to your project
        // .clk_flags = 0,          /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
    };
    if(i2c_param_config(OLED_I2C_POER_NUM, &conf) != ESP_OK) {
        return 1;
	}

    if(i2c_driver_install(OLED_I2C_POER_NUM, I2C_MODE_MASTER, 0, 0, 0) != ESP_OK) {
        return 2;
	}

    if(i2c_master_write_slave(OLED_I2C_POER_NUM, oled_init_cmd, sizeof(oled_init_cmd)) != ESP_OK) {
        return 3;
	}

    return ESP_OK;
}
